import Foundation

enum 科目: String, CaseIterable {
    case 语文,数学,英语,物理
}

typealias ScoresDic = [科目 : Double]

struct Student {
    let name: String
    let scores: ScoresDic
}

//学生模拟数据
let s1 = Student(
    name: "Jane",
    scores: [.语文: 86, .数学: 92, .英语: 73, .物理: 88]
)
let s2 = Student(
    name: "Tom",
    scores: [.语文: 99, .数学: 52, .英语: 97, .物理: 36]
)
let s3 = Student(
    name: "Emma",
    scores: [.语文: 91, .数学: 92, .英语: 100, .物理: 99]
)
let students = [s1, s2, s3]
//

/*
 我们现在想要检查 students 里的学生的平均分，并输出第一名的。使用指令式的方式，依靠运算，循环和条件语句，可以给出下面这种解决方案：
*/
do {
    var best: (Student, Double)? //第一名的学生信息及其平均分
    for student in students {
        var totalScore: Double = 0
        for key in 科目.allCases {
            totalScore += student.scores[key] ?? 0
        } //获取所有科目的总成绩
        let averageScore = totalScore / Double(科目.allCases.count) //平均分
        
        //第一名学生的判定
        if let temp = best {
            if averageScore > temp.1 {
                best = (student, averageScore)
            }
        } else {
            best = (student, averageScore)
        }
    }
    
    if let best = best {
        print("指令式编程")
        print("最高平均分: \(best.1), 姓名: \(best.0.name)")
    } else {
        print("students 为空")
    }
}
/**
 如果第一次读这段代码的话，想要了解它到底做了什么或者到底最后会得到怎样的结果，可能必须要仔细阅读并理解每一行指令。
 代码行数与 bug 多少往往是正相关，错误很可能隐藏在某个循环或者判断里，这种开发方式为代码的正确性和后续维护带来了巨大的挑战
 
 我们有什么办法可以减轻开发者的负担，让计算机更加“智能”地为我们解决问题呢？
 */






//使用函数式编程
do {
    
    func average(_ scores: ScoresDic) -> Double {
        scores.values.reduce(0, +) / Double(科目.allCases.count)
    }
    
    let bestStudent = students
        .map { ($0, average($0.scores)) } //映射 (学生，平均分)
        .sorted { $0.1 > $1.1 } //排序
        .first
    
    if let bestStudent = bestStudent {
        print("函数式编程")
        print("最高平均分: \(bestStudent.1), 姓名: \(bestStudent.0.name)")
    } else {
        print("students 为空")
    }
}
/**
 在这段代码中，我们首先将 students 映射为了 (Student, 平均分) 的数组，
 然后对平均分按降序进行排序，
 最后取出排序后的首个元素。
 在这个过程中，我们仅仅是用语句描述了我们想要的结果，例如：按规则进行映射、对元素进行排序等。我们并不关心代码在底层具体是如何操作数组的，而只关心这段代码能够得到我们所描述的结果。
 */
